const Get = require("lodash/get");
const _Map = require("lodash/map");
const Last = require("lodash/last");

type RenderFileFn = (err: any, str: string) => void

type TemplateFn = (data: any) => string

type Token = {
    type: TokenType,
    text: string
}

enum TokenType {
    normal = 0,
    showVariate = 1,
    expression = 2
}

class sjs {
    static _flag = {
        start: "<%",
        close: "%>",
        matchReg: /<%[=\-_]? (.*)%>/g
    };

    /**
     *  @param {string} html html文本
     *  @returns {string[]}
     *  @private
     */
    private static getHtmlToken(html: string): Token[] {
        let htmlToken = [
            {
                type: TokenType.normal,
                text: ""
            }
        ];
        let htmlArr = Array.from(html);
        //遍历字符串 分割
        htmlArr.forEach((key, index) => {
            let nextKey = Get(htmlArr, [index + 1], "");
            let token = key + nextKey;
            let prevToken = index >= 2 ? `${htmlArr[index - 2]}${htmlArr[index - 1]}` : "";

            //根据token 来进行分割
            switch (token) {
                case sjs._flag.close:
                    htmlToken.push({
                        type: TokenType.normal,
                        text: ""
                    });
                    break;
                case sjs._flag.start:
                    //操作符号
                    let operationFlag = Get(htmlArr, [index + 2], "");
                    switch (operationFlag) {
                        case "=":
                            htmlToken.push({
                                type: TokenType.showVariate,
                                text: ""
                            });
                            break;
                        case " ":
                            htmlToken.push({
                                type: TokenType.expression,
                                text: ""
                            });
                            break;
                    }
                    break;
            }

            let currentHtmlArr = Last(htmlToken);

            if (prevToken === sjs._flag.close) {
                let currentArrIndex = htmlToken.length - 2 >= 0 ? htmlToken.length - 2 : htmlToken.length - 1;
                //防止错误
                htmlToken[currentArrIndex].text += "%>";
                //去掉头部
                currentHtmlArr.text = "";
            }
            currentHtmlArr.text += key;
        });



        return htmlToken
    }

    private static createRenderFn(tokens: Token[],data): any {
        let paramsName = (function(){
            let paramsKeys = _Map(data,function (val,keyname) {
                return keyname;
            });
            if(paramsKeys.length > 0){
                return paramsKeys.join(",")
            }else{
                return  ""
            }
        })();
        let fnBody = tokens.map(token => {
            switch (token.type) {
                case TokenType.normal:
                     return "__html +=" +  "`" + token.text + "`;";
                case TokenType.expression:
                     return  token.text.replace(sjs._flag.matchReg,"$1") + ";";
                case TokenType.showVariate:
                    let variate = token.text.replace(sjs._flag.matchReg,"$1");
                    return `__html += ${variate};`
            }
        }).join("");
        let fn = `
                (function(){
                   return function({${paramsName}}){
                       let __html = "";
                       ${fnBody}
                       return __html;
                   } 
                })()
           `;
        return eval(fn);
    }

    static compile(str: string, options: any): TemplateFn {
        let _options = options;
        return function (data: any) {
            return sjs.render(str, data, _options)
        }
    }

    static render(str: string, data: any, options: any): string {
        let tokens = sjs.getHtmlToken(str);
        let renderFn = sjs.createRenderFn(tokens,data);
        return renderFn(data)
    }

    static renderFile(filename: string, data: any, options: any, fn: RenderFileFn): void {

    }
}

module.exports = sjs;